using Nop.Core;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Localization;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Tax;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Logistics;
using Nop.Services.Messages;
using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Seo;
using Nop.Services.Tax;
using Nop.Core.Domain.Logistics;
using Spotshub.Api.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Mvc;
using Nop.Data;
using Spotshub.Api.Filter;

namespace Spotshub.Api.Controllers.V1
{
    [ExceptionHandler]
    public class ReturnRequestController : ApiController
    {
        private const string AddressStringFormat = "{0}\r\n {1}\r\n{2} {3} {4}";

        private readonly IOrderService _orderService;
        private readonly IWorkContext _workContext;
        private readonly ICurrencyService _currencyService;
        private readonly IPriceFormatter _priceFormatter;

        private readonly ILocalizationService _localizationService;
        private readonly ICustomerService _customerService;
        private readonly IWorkflowMessageService _workflowMessageService;

        private readonly LocalizationSettings _localizationSettings;
        private readonly OrderSettings _orderSettings;
        private readonly Nop.Services.Orders.IOrderProcessingService _orderProcessingOrdersService;
        private readonly Nop.Services.Logistics.IOrderProcessingService _orderProcessingLogisticsService;

        private readonly IAddressService _addressService;
        private readonly AddressSettings _addressSettings;
        private readonly IStateProvinceService _stateProvinceService;
        private readonly ICountryService _countryService;

        private readonly Nop.Services.Logistics.IOrderProcessingService _orderProcessingService;
        private readonly ILogisticsService _logisticsService;
        private readonly RewardPointsSettings _rewardPointsSettings;
        private readonly IOrderTotalCalculationService _orderTotalCalculationService;
        private readonly PaymentSettings _paymentSettings;
        private readonly IPaymentService _paymentService;
        private readonly ITaxService _taxService;
        private readonly IGenericAttributeService _genericAttributeService;
        private readonly ILogger _logger;
        private readonly IMobileDeviceHelper _mobileDeviceHelper;
        private readonly IInvoiceService _invoiceService;
        private readonly IProductAttributeParser _attributeParser;

        public ReturnRequestController(IOrderService orderService,
          IWorkContext workContext,
          ICurrencyService currencyService, IPriceFormatter priceFormatter,
          Nop.Services.Orders.IOrderProcessingService orderProcessingOrdersService,
          Nop.Services.Logistics.IOrderProcessingService orderProcessingLogisticsService,
          ILocalizationService localizationService,
          ICustomerService customerService,
          IWorkflowMessageService workflowMessageService,
          LocalizationSettings localizationSettings,
          OrderSettings orderSettings, AddressSettings addressSettings, IStateProvinceService stateProvinceService
          , ICountryService countryService, Nop.Services.Logistics.IOrderProcessingService orderProcessingService,
          ILogisticsService logisticsService, IAddressService addressService
          , RewardPointsSettings rewardPointsSettings, IOrderTotalCalculationService orderTotalCalculationService,
          PaymentSettings paymentSettings, IPaymentService paymentService, ITaxService taxService,
          IGenericAttributeService genericAttributeService, ILogger logger, IMobileDeviceHelper mobileDeviceHelper,
          IInvoiceService invoiceService, IProductAttributeParser attributeParser)
        {
            this._orderService = orderService;
            this._workContext = workContext;
            this._currencyService = currencyService;
            this._priceFormatter = priceFormatter;
            this._orderProcessingOrdersService = orderProcessingOrdersService;
            this._orderProcessingLogisticsService = orderProcessingLogisticsService;
            this._localizationService = localizationService;
            this._customerService = customerService;
            this._workflowMessageService = workflowMessageService;

            this._localizationSettings = localizationSettings;
            this._orderSettings = orderSettings;
            this._addressSettings = addressSettings;
            this._stateProvinceService = stateProvinceService;
            this._countryService = countryService;
            this._orderProcessingService = orderProcessingService;
            this._logisticsService = logisticsService;
            this._addressService = addressService;
            this._rewardPointsSettings = rewardPointsSettings;
            this._orderTotalCalculationService = orderTotalCalculationService;
            this._paymentSettings = paymentSettings;
            this._paymentService = paymentService;
            this._taxService = taxService;
            this._genericAttributeService = genericAttributeService;
            this._logger = logger;
            this._mobileDeviceHelper = mobileDeviceHelper;
            this._invoiceService = invoiceService;
            this._attributeParser = attributeParser;
        }

        [System.Web.Http.HttpGet]
        public ReturnRequestModel ReturnRequest(int orderId)
        {
            var model = new ReturnRequestModel();
            var order = _orderService.GetOrderById(orderId);
            var customer = _workContext.CurrentCustomer;
            if (order == null || order.Deleted || _workContext.CurrentCustomer.Id != order.CustomerId)
            {
                model.Success = false;
                model.Errors.Add("Not Authorized!");
                return model;
            }

            model.OrderId = orderId;
            PrepareReturnRequestModel(model, order);
            model.Success = true;
            return model;
        }

        [System.Web.Http.HttpPost]
        public SubmitReturnRequestResult SubmitReturnRequest([FromBody]SubmitReturnRequestModel request)
        {
            var result = new SubmitReturnRequestResult();
            var customer = _workContext.CurrentCustomer;
            var order = _orderService.GetOrderById(request.OrderId);

            if (order == null || order.Deleted || _workContext.CurrentCustomer.Id != order.CustomerId)
            {
                result.Success = false;
                result.Errors.Add("Not Authorized.");
                return result;
            }

            if (customer.ShippingAddress == null || customer.BillingAddress == null)
            {
                result.Success = false;
                result.Errors.Add("Not shipping address or billing address.");
                return result;
            }

            if (!_orderProcessingOrdersService.IsReturnRequestAllowed(order))
            {
                result.Success = false;
                result.Errors.Add("Unable to process the return request.");
                return result;
            }


            int count = 0;
            var rrs = new List<ReturnRequest>();

            foreach (var opv in order.OrderProductVariants)
            {
                int quantity = 0; //parse quantity
                var pv = request.Items.FirstOrDefault(ite => ite.ProductId == opv.ProductVariantId);
                if (pv != null)
                {
                    quantity = pv.Quantity;
                }
                if (quantity > 0)
                {
                    rrs.Add(new ReturnRequest()
                    {
                        OrderProductVariantId = opv.Id,
                        Quantity = quantity,
                        CustomerId = _workContext.CurrentCustomer.Id,
                        ReasonForReturn = request.ReturnReason,
                        RequestedAction = request.ReturnAction,
                        CustomerComments = request.Comments,
                        StaffNotes = string.Empty,
                        ReturnRequestStatus = ReturnRequestStatus.Pending,
                        CreatedOnUtc = DateTime.UtcNow,
                        UpdatedOnUtc = DateTime.UtcNow
                    });

                    //refund opv
                    opv.RefundQuantity = opv.RefundQuantity.HasValue ? (opv.RefundQuantity.Value + quantity) : quantity;
                    _orderService.UpdateOrder(order);

                    count++;
                }
            }

            if (count <= 0)
            {
                result.Errors.Add(_localizationService.GetResource("ReturnRequests.NoItemsSubmitted"));
                result.Success = false;
            }
            else
            {
                var customerUser = _customerService.GetCustomerById(_workContext.CurrentCustomer.Id);
                rrs.ForEach(x => customerUser.ReturnRequests.Add(x));
                _customerService.UpdateCustomer(customerUser);

                result.Success = true;
            }

            return result;
        }

        [System.Web.Http.HttpGet]
        public IList<UserReturnRequestModel> GetAll()
        {
            var result = new List<UserReturnRequestModel>();
            if (!_workContext.CurrentCustomer.IsGuest() && _workContext.CurrentCustomer.ReturnRequests != null)
            {
                result.AddRange(_workContext.CurrentCustomer.ReturnRequests.Select(rr => new UserReturnRequestModel()
                {
                    OrderProductId = rr.OrderProductVariantId,
                    Quantity = rr.Quantity,
                    ReasonForReturn = rr.ReasonForReturn,
                    RequestedAction = rr.RequestedAction,
                    CustomerComments = rr.CustomerComments,
                    StaffNotes = rr.StaffNotes,
                    CreatedOnUtc = rr.CreatedOnUtc,
                    UpdatedOnUtc = rr.UpdatedOnUtc,
                    ReturnRequestStatus = rr.ReturnRequestStatus.ToString()
                }));
            }
            return result;
        }

        private void PrepareReturnRequestModel(ReturnRequestModel model, Order order)
        {
            model.ShipmentMethods.Add(new SelectListItem() { Text = _localizationService.GetResource("commerce.return.shipment.spotshubexpress"), Value = "1", Selected = true });
            model.ShipmentMethods.Add(new SelectListItem() { Text = _localizationService.GetResource("commerce.return.shipment.3rdparty"), Value = "2", Selected = false });

            if (_orderSettings.ReturnRequestReasons != null)
            {
                _orderSettings.ReturnRequestReasons.ForEach(r => model.AvailableReasons.Add(r));
            }

            if (_orderSettings.ReturnRequestActions != null)
            {
                _orderSettings.ReturnRequestActions.ForEach(r => model.AvailableActions.Add(r));
            }

            if (_workContext.CurrentCustomer.Addresses != null)
            {
                foreach (var addr in _workContext.CurrentCustomer.Addresses)
                    model.Addresses.Add(new Nop.Core.ApiDataModel.Address(addr));
            }

            var orderProductVariants = _orderService.GetAllOrderProductVariants(order.Id, null, null, null, null, null, null)
                .Where(opv => !opv.RefundQuantity.HasValue || opv.Quantity > opv.RefundQuantity.Value);

            foreach (var opv in orderProductVariants)
            {
                var opvModel = new SubmitReturnRequestModel.OrderProductModel()
                {
                    ProductId = opv.ProductVariantId,
                    Quantity = opv.RefundQuantity.HasValue ? (opv.Quantity - opv.RefundQuantity.Value) : opv.Quantity
                };

                if (!string.IsNullOrEmpty(opv.AttributesXml))
                {
                    var attributes = new List<KeyValuePair<string, string>>();
                    var values = this._attributeParser.ParseProductVariantAttributeValues(opv.AttributesXml);
                    foreach (var val in values)
                    {
                        attributes.Add(new KeyValuePair<string, string>(val.ProductVariantAttribute.ProductAttribute.Name, val.Name));
                    }
                    opvModel.AttributeInfo = attributes;
                }

                //product name
                if (!String.IsNullOrEmpty(opv.ProductVariant.GetLocalized(x => x.Name)))
                    opvModel.ProductName = string.Format("{0} ({1})", opv.ProductVariant.Product.GetLocalized(x => x.Name), opv.ProductVariant.GetLocalized(x => x.Name));
                else
                    opvModel.ProductName = opv.ProductVariant.Product.GetLocalized(x => x.Name);
                model.Items.Add(opvModel);

                //unit price
                switch (order.CustomerTaxDisplayType)
                {
                    case TaxDisplayType.ExcludingTax:
                        {
                            var opvUnitPriceExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(opv.UnitPriceExclTax, order.CurrencyRate);
                            opvModel.UnitPrice = _priceFormatter.FormatPrice(opvUnitPriceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, _workContext.WorkingLanguage, false);
                        }
                        break;
                    case TaxDisplayType.IncludingTax:
                        {
                            var opvUnitPriceInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(opv.UnitPriceInclTax, order.CurrencyRate);
                            opvModel.UnitPrice = _priceFormatter.FormatPrice(opvUnitPriceInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, _workContext.WorkingLanguage, true);
                        }
                        break;
                }
            }
        }
    }
}
